﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace H.Utils.DataStruct
{
    /// <summary>
    ///TODO: 需要改进,值类型不应该进行封装
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="V"></typeparam>
    public class WeakHashMap<K, V> : IDictionary<K, V>
    {
        /// <summary>
        /// 用于存储数据的HashTable
        /// </summary>
        private readonly Hashtable _Table = new Hashtable();

        public V this[K key]
        {
            get
            {
                WeakObject<V> value = _Table[key] as WeakObject<V>;

                if (value.IsAlive)
                {
                    return (V)value.Target;
                }
                else
                {
                    _Table.Remove(key);
                    return default;
                }
            }
            set => _Table[new WeakObject<K>(key)] = new WeakObject<V>(value);
        }

        public ICollection<K> Keys => _Table.Keys.OfType<WeakObject<K>>().Where(k => k.IsAlive).Select(k => k.Target).OfType<K>().ToList();
        public ICollection<V> Values => _Table.Values.OfType<WeakObject<V>>().Where(v => v.IsAlive).Select(v => v.Target).OfType<V>().ToList();

        public int Count => _Table.Count;

        public bool IsReadOnly => false;

        public void Add(K key, V value)
        {
            _Table.Add(key, new WeakObject<V>(value));
        }

        public void Add(KeyValuePair<K, V> item)
        {
            _Table.Add(item.Key, new WeakObject<V>(item.Value));
        }

        public void Clear()
        {
            _Table.Clear();
        }

        public bool Contains(KeyValuePair<K, V> item)
        {
            return _Table.ContainsKey(item.Key);
        }
        public bool Contains(WeakObject<K> key)
        {
            return _Table.ContainsKey(key);
        }

        public bool ContainsKey(K key)
        {
            return _Table.ContainsKey(key);
        }

        public bool ContainsKey(WeakObject<K> key)
        {
            return _Table.ContainsKey(key);
        }

        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
        {
            foreach (KeyValuePair<WeakObject<K>, WeakObject<V>> kv in _Table)
            {
                if (kv.Key.IsAlive && kv.Value.IsAlive)
                {
                    array[arrayIndex] = new KeyValuePair<K, V>((K)kv.Key.Target, (V)kv.Value.Target);
                    ++arrayIndex;
                    if (arrayIndex >= array.Length)
                    {
                        break;
                    }
                }
                else
                {
                    _Table.Remove(kv.Key);
                }
            }
        }

        public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
        {
            foreach (KeyValuePair<WeakObject<K>, WeakObject<V>> kv in _Table)
            {
                if (kv.Key.IsAlive && kv.Value.IsAlive)
                {
                    yield return new KeyValuePair<K, V>((K)kv.Key.Target, (V)kv.Value.Target);
                }
            }
        }

        public bool Remove(K key)
        {
            _Table.Remove(key);
            return true;
        }

        public bool Remove(KeyValuePair<K, V> item)
        {
            _Table.Remove(item.Key);
            return true;
        }

        public bool TryGetValue(K key, out V value)
        {
            WeakObject<V> v = _Table[key] as WeakObject<V>;

            if (v.IsAlive)
            {
                value = (V)v.Target;
                return true;
            }
            else
            {
                value = default;
                return false;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (KeyValuePair<WeakObject<K>, WeakObject<V>> kv in _Table)
            {
                if (kv.Key.IsAlive && kv.Value.IsAlive)
                {
                    yield return new KeyValuePair<K, V>((K)kv.Key.Target, (V)kv.Value.Target);
                }
            }
        }
    }
}
